//
//  XKSecurityCode.m
//  XKSecurityCenter
//
//  Created by xiaoka on 2019/2/20.
//  Copyright © 2019年 xiaoka. All rights reserved.
//

#import "XKSecurityCode.h"
#import <UIKit/UIKit.h>
#import <dlfcn.h>
#import <mach-o/loader.h>
#import <mach-o/dyld.h>
#import <mach-o/arch.h>
#import <sys/stat.h>
#include <sys/sysctl.h>


@implementation XKSecurityCode


#if !defined(PT_DENY_ATTACH)
#define PT_DENY_ATTACH 31
#endif  // !defined(PT_DENY_ATTACH)
typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);

void disable_gdb() {
    
    
    void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
    ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
    ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
    dlclose(handle);
}

+ (void)load
{
#ifndef DEBUG
    disable_gdb();
   
  #ifdef __arm__
    asm volatile(
                 "mov r0,#31\n"
                 "mov r1,#0\n"
                 "mov r2,#0\n"
                 "mov r12,#26\n"
                 "svc #80\n"
                 );
  #endif
    
  #ifdef __arm64__
    asm volatile(
                 "mov x0,#26\n"
                 "mov x1,#31\n"
                 "mov x2,#0\n"
                 "mov x3,#0\n"
                 "mov x16,#0\n"
                 "svc #128\n"
                 );
  #endif
    
#endif
    

}

+ (XKSecurityCode *)defaultSingleton
{
    static dispatch_once_t onceToken;
    static XKSecurityCode * defaultSingleton = nil;
    dispatch_once(&onceToken, ^{
        defaultSingleton = [[self alloc] init];
    });
    
    return defaultSingleton;
}

- (void)observerScreensWithCompletionHandler:(void(^)(void))completionHandler
{
    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification object:nil queue:mainQueue usingBlock:^(NSNotification *note) {
        
        /// 截屏已经发生,可进行相关提示处理
        completionHandler();
    }];
    
}


static __inline__ __attribute__((always_inline)) int anti_tweak()
{
    uint8_t lmb[] = {'S', 'u', 'b', 's', 't', 'r', 'a', 't', 'e', '/', 'D', 'y', 'n', 'a', 'm', 'i', 'c', 0, };
    NSString *dir = [NSString stringWithFormat:@"/%@/%@%s%@", @"Library", @"Mobile", lmb, @"Libraries"];
    NSArray *dirFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:nil];
    NSArray *plistFiles = [dirFiles filteredArrayUsingPredicate:
                               [NSPredicate predicateWithFormat:
                                [NSString stringWithFormat:@"%@ %@%@ '.%@%@'",@"self", @"EN", @"DSWITH", @"pli", @"st"]]];
    
    int cnt = 0;
    for (NSString *file in plistFiles) {
        NSString *filePath = [dir stringByAppendingPathComponent:file];
        NSString *fileContent = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
        if (fileContent && [fileContent rangeOfString:[[NSBundle mainBundle] bundleIdentifier]].location != NSNotFound) {
            cnt ++;
        }
    }
    // 返回有针对本 app 的 tweak 数量，为 0 说明没有
    return cnt;
}


- (BOOL)checkAppHasTweak
{
    return anti_tweak() == 0 ? NO : YES;
}


- (BOOL)checkAppJailbreak
{
    NSArray * jailbreakArr = @[
      @"/Applications/Cydia.app",
      @"/Library/MobileSubstrate/MobileSubstrate.dylib",
      @"/bin/bash",
      @"/usr/sbin/sshd",
      @"/etc/apt"
      ];
    
    BOOL isOpen_one = NO;
    BOOL isOpen_two = NO;
    BOOL isOpen_three = NO;
    BOOL isOpen_four = NO;
    BOOL isOpen_five = NO;
    
    for (int i = 0; i < jailbreakArr.count; i ++) {
        if ([[NSFileManager defaultManager] fileExistsAtPath:jailbreakArr[i]]) {
            isOpen_one = YES;
        }
    }
    
    if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://"]]) {
        /// 如果openurl可以打开 则代表是越狱环境
        isOpen_two = YES;
    }
    
    if ([[NSFileManager defaultManager] fileExistsAtPath:@"User/Applications/"]) {
        
        NSArray *appList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"User/Applications/" error:nil];
        /// 没有越狱是取不到User/Applications/路径的或者是User/Applications/ 路径下没有文件
        /// 如果能存在这个路径并且在路径下有文件 则代表是越狱环境
        if (appList.count > 0) {
            isOpen_three = YES;
       }
    }
    
    if (getenv("DYLD_INSERT_LIBRARIES")) {
        isOpen_four = YES;
    }
    
    if(!TARGET_IPHONE_SIMULATOR) {
        char *JailbrokenPathArr[] = {"/Applications/Cydia.app","/usr/sbin/sshd","/bin/bash","/etc/apt","/Library/MobileSubstrate","/User/Applications/"};
        
        for (int i = 0;i < sizeof(JailbrokenPathArr) / sizeof(char *);i++) {
            struct stat stat_info_a;
            if (0 == stat(JailbrokenPathArr[i], &stat_info_a)) {
                isOpen_five = YES;
            }
        }
    }
        
    
    if (isOpen_one || isOpen_two || isOpen_three || isOpen_four || isOpen_five) {
        return YES;
    }
    return NO;
}


- (BOOL)isDebuggerPresent {
    int                 junk;
    int                 mib[4];
    struct kinfo_proc   info;
    size_t              size;
 
    info.kp_proc.p_flag = 0;
 
    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_PID;
    mib[3] = getpid();
 
    // Call sysctl.
 
    size = sizeof(info);
    junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
    assert(junk == 0);
 
    return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
}


- (BOOL)checkInjector {
    
    if(TARGET_IPHONE_SIMULATOR) return NO;
        
    int dyld_count = _dyld_image_count();
    for (int i = 0; i < dyld_count; i++) {
        const char * imageName = _dyld_get_image_name(i);
        NSString *res = [NSString stringWithUTF8String:imageName];
                
        if(![res hasSuffix:@".dylib"]){
            continue;
        }
        
        if ([res containsString:@"/Library/MobileSubstrate/DynamicLibraries"]) {
            if ([res lastPathComponent]) {
                return YES;
            }
        }
    }
    return NO;
}
    
    

@end
